// Register a new language
let languageName = "ql-language";
let languageTheme = "ql-theme";
monaco.languages.register({ id: languageName});

// 设置配对模式
monaco.languages.setLanguageConfiguration(languageName,{
	wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
            comments: {
                lineComment: "//",
                blockComment: ["/*", "*/"]
            },
            brackets: [["{", "}"], ["[", "]"], ["(", ")"]],
            autoClosingPairs: [{open: "{",close: "}"}, {open: "[",close: "]"}, {open: "(",close: ")"}, {open: '"',close: '"'}, {open: "'",close: "'"}],
            surroundingPairs: [{open: "{",close: "}"}, {open: "[",close: "]"}, {open: "(",close: ")"}, {open: '"',close: '"'}, {open: "'",close: "'"}, {open: "<",close: ">"}],
            folding: {
                markers: {
                    start: new RegExp("^\\s*//\\s*(?:(?:#?region\\b)|(?:<editor-fold\\b))"),
                    end: new RegExp("^\\s*//\\s*(?:(?:#?endregion\\b)|(?:</editor-fold>))")
                }
            }
});

// 注册并定义一个语言及语法等
monaco.languages.setMonarchTokensProvider(languageName, {
    ignoreCase:true,
    defaultToken: '',
    tokenPostfix: '.java',

    keywords: [
        'abstract', 'continue', 'for', 'new', 'switch', 'assert', 'default',
        'goto', 'package', 'synchronized', 'boolean', 'do', 'if', 'private',
        'this', 'break', 'double', 'implements', 'protected', 'throw', 'byte',
        'else', 'import', 'public', 'throws', 'case', 'enum', 'instanceof', 'return',
        'transient', 'catch', 'extends', 'int', 'short', 'try', 'char', 'final',
        'interface', 'static', 'void', 'class', 'finally', 'long', 'strictfp',
        'volatile', 'const', 'float', 'native', 'super', 'while', 'true', 'false','system',
        'function'
    ],

    operators: [
        '=', '>', '<', '!', '~', '?', ':',
        '==', '<=', '>=', '!=', '&&', '||', '++', '--',
        '+', '-', '*', '/', '&', '|', '^', '%', '<<',
        '>>', '>>>', '+=', '-=', '*=', '/=', '&=', '|=',
        '^=', '%=', '<<=', '>>=', '>>>='
    ],

    // we include these common regular expressions
	//brackets: [["{", "}",'delimiter.curly'], ["[", "]",'delimiter.curly'], ["(", ")",'delimiter.curly']],
    symbols: /[=><!~?:&|+\-*\/\^%]+/,
    escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
    digits: /\d+(_+\d+)*/,
    octaldigits: /[0-7]+(_+[0-7]+)*/,
    binarydigits: /[0-1]+(_+[0-1]+)*/,
    hexdigits: /[[0-9a-fA-F]+(_+[0-9a-fA-F]+)*/,

    // The main tokenizer for our languages
    tokenizer: {
        root: [
			["non-sealed", "keyword.non-sealed"],
            // identifiers and keywords
            [/[a-zA-Z_$][\w$]*/, {
                cases: {
                    '@keywords': { token: 'keyword.$0' },
                    '@default': 'identifier'
                }
            }],

            // whitespace
            { include: '@whitespace' },

            // delimiters and operators
            [/[{}()\[\]]/, '@brackets'],
            [/[<>](?!@symbols)/, '@brackets'],
            [/@symbols/, {
                cases: {
                    '@operators': 'delimiter',
                    '@default': ''
                }
            }],

            // @ annotations.
            [/@\s*[a-zA-Z_\$][\w\$]*/, 'annotation'],

            // numbers
            [/(@digits)[eE]([\-+]?(@digits))?[fFdD]?/, 'number.float'],
            [/(@digits)\.(@digits)([eE][\-+]?(@digits))?[fFdD]?/, 'number.float'],
            [/0[xX](@hexdigits)[Ll]?/, 'number.hex'],
            [/0(@octaldigits)[Ll]?/, 'number.octal'],
            [/0[bB](@binarydigits)[Ll]?/, 'number.binary'],
            [/(@digits)[fFdD]/, 'number.float'],
            [/(@digits)[lL]?/, 'number'],

            // delimiter: after number because of .\d floats
            [/[;,.]/, 'delimiter'],

            // strings
            [/"([^"\\]|\\.)*$/, 'string.invalid'],  // non-teminated string
            [/#{.*}/, 'string.invalid'],  // non-teminated string
            [/"/, 'string', '@string'],

            // characters
            [/'[^\\']'/, 'string'],
            [/(')(@escapes)(')/, ['string', 'string.escape', 'string']],
            [/'/, 'string.invalid']
        ],

        whitespace: [
            [/[ \t\r\n]+/, ''],
            [/\/\*\*(?!\/)/, 'comment.doc', '@javadoc'],
            [/\/\*/, 'comment', '@comment'],
            [/\/\/.*$/, 'comment'],
        ],

        comment: [
            [/[^\/*]+/, 'comment'],
            // [/\/\*/, 'comment', '@push' ],    // nested comment not allowed :-(
            // [/\/\*/,    'comment.invalid' ],    // this breaks block comments in the shape of /* //*/
            [/\*\//, 'comment', '@pop'],
            [/[\/*]/, 'comment']
        ],
        //Identical copy of comment above, except for the addition of .doc
        javadoc: [
            [/[^\/*]+/, 'comment.doc'],
            // [/\/\*/, 'comment.doc', '@push' ],    // nested comment not allowed :-(
            [/\/\*/, 'comment.doc.invalid'],
            [/\*\//, 'comment.doc', '@pop'],
            [/[\/*]/, 'comment.doc']
        ],

        string: [
            [/[^\\"]+/, 'string'],
            [/@escapes/, 'string.escape'],
            [/\\./, 'string.escape.invalid'],
            [/"/, 'string', '@pop']
        ],

        string_double: [
            [/[^\\"]+/, 'string'],
            [/@escapes/, 'string.escape'],
            [/\\./, 'string.escape.invalid'],
            [/"/, 'string', '@pop']
        ],

        string_single: [
            [/[^\\']+/, 'string'],
            [/@escapes/, 'string.escape'],
            [/\\./, 'string.escape.invalid'],
            [/'/, 'string', '@pop']
        ],

        string_backtick: [
            [/\$\{/, { token: 'delimiter.bracket', next: '@bracketCounting' }],
            [/[^\\`$]+/, 'string'],
            [/@escapes/, 'string.escape'],
            [/\\./, 'string.escape.invalid'],
            [/`/, 'string', '@pop']
        ],

        bracketCounting: [
            [/\{/, 'delimiter.bracket', '@bracketCounting'],
            [/\}/, 'delimiter.bracket', '@pop']
        ],
    },
});

//主题定义
monaco.editor.defineTheme(languageTheme, {
    base: 'vs-dark',
    inherit: true,
    rules: [{ background: 'EDF9FA' }],
    colors: {
        'editor.background': '#2b2b2b'
    }
});

// 设置语法自动提示的功能
monaco.languages.registerCompletionItemProvider(languageName, {
    triggerCharacters:['.',' '], // 触发自动提示的关键字
    provideCompletionItems(model, position,item,token) {

        //当前光标所在的单词
        let word = model.getWordUntilPosition(position);

        let range = {
            startLineNumber: position.lineNumber,
            endLineNumber: position.lineNumber,
            startColumn: word.startColumn,
            endColumn: word.endColumn
        }

        //import 匹配
        let lineContent = model.getLineContent(position.lineNumber);
        if (lineContent.indexOf("import ") == 0){
            return {
                suggestions: provideCompletionImport(range)
            };
        }

        //方法自动提示
        //最后输入是否为 "."
        let point = model.getValueInRange({
            startLineNumber: position.lineNumber,
            endLineNumber: position.lineNumber,
            startColumn: position.column-1,
            endColumn: position.column
        });

        //最后输入"."前的变量
        let varNamePoint = model.getValueInRange({
            startLineNumber: position.lineNumber,
            endLineNumber: position.lineNumber,
            startColumn: word.startColumn-1,
            endColumn: word.startColumn
        });

        if (point == "." || varNamePoint == "."){
            let varName = null;
            if (point == "."){
                varName = model.getWordUntilPosition({"lineNumber": position.lineNumber, "column": position.column-1}).word;
                if(!varName){
                    varName = lineContent.substring(position.column-3,position.column-2);
                }
            }else{
                varName = model.getWordUntilPosition({"lineNumber": position.lineNumber, "column": word.startColumn-1}).word;
            }

            if (varName){
                return {
                    suggestions: provideCompletionFunc(model.getValue(),varName,lineContent,position)
                };
            }
        }

        //变量命名自动生成
        let definition = model.getWordUntilPosition({
            column:word.startColumn-1,
            lineNumber:position.lineNumber
        })
        if (definition.word && definition.word.substring(0,1).match("[A-Z]")){
            return {
                suggestions: provideCompletionDefinition(range,definition.word)
            };
        }

        //类型匹配
        return {
            suggestions: provideCompletionTypes(range,word.word,model.getValue(),lineContent,position)
        };

    }
});

function toHump(name) {
    return name.replace(/\_(\w)/g, function(all, letter){
        return letter.toUpperCase();
    });
}

/*自动完成import语法*/
function provideCompletionImport(range) {
    let suggestions = [];
    if (!completionItems.clazzs){
        return suggestions;
    }

    $.each(completionItems.clazzs,function (key,value) {
        let label = key.substring(key.lastIndexOf(".")+1);
        let detail = key.substring(0,key.lastIndexOf("."));
        suggestions.push({
            label: label,
            kind: monaco.languages.CompletionItemKind.Class,
            detail: detail,
            insertText: key+";\n",
            filterText: buildFilterText(label),
            insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
            range: range
        })
    })
    return suggestions;
}

/*将定义的变量放进提示中,方便代码中使用*/
function provideCompletionDefinition(range,word) {
    let suggestions = [];
    //首字母小写
    let str = "";
    for (let i=word.length-1;i>=0;i--){
        str = word.charAt(i) + str;
        if (word.charAt(i).match("[A-Z]")){
            let itemWord = str.replace(str[0],str[0].toLowerCase());
            suggestions.push({
                label: itemWord,
                kind: monaco.languages.CompletionItemKind.Variable,
                detail: "",
                filterText:buildFilterText(itemWord),
                insertText: itemWord,
                insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
            })
        }
    }

    return suggestions;
}

/*上下文及内嵌信息*/
function provideCompletionTypes(range,word,fullValue,lineContent,position){

    let suggestions = [];
    if (!completionItems){
        return suggestions;
    }

    //当前上下文变量
    let regex = /[a-zA-Z0-9_]+ +=/gim;
    let match = null;
    let wordSet = new Set();
    while((match = regex.exec(fullValue)) != null){
        let item = match[0].substring(0,match[0].indexOf(" "));
        if (word && word == item){
            continue;
        }
        wordSet.add(item)
    }
    regex = /[a-zA-Z0-9_]+ *;/gim;
    while((match = regex.exec(fullValue)) != null){
        let item = match[0].substring(0,match[0].indexOf(";"));
        if (word && word == item){
            continue;
        }
        wordSet.add(item.trim())
    }
	
    wordSet.forEach(function (index,item) {
        suggestions.push({
            label: item,
            kind: monaco.languages.CompletionItemKind.Variable,
            detail: item,
            insertText: item,
            filterText: buildFilterText(item),
            insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
            range: range
        })
    })

    //语法
    $.each(completionItems.syntax,function (key,value) {
        suggestions.push({
            label: key,
            kind: monaco.languages.CompletionItemKind.Struct,
            detail: value,
            insertText: value,
            filterText: buildFilterText(key),
            insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
            range: range
        })
    });

    //内置变量
    $.each(completionItems.variables,function (key,value) {
        suggestions.push({
            label: key,
            kind: monaco.languages.CompletionItemKind.Variable,
            detail: value,
            insertText: key,
            filterText: buildFilterText(key),
            insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
            range: range
        })
    });
	
	//内置方法
    $.each(completionItems.functions,function (idx,item) {
        let key = item.varName;
        suggestions.push({
            label: key+"("+item.params+")",
            kind: monaco.languages.CompletionItemKind.Function,
            detail: item.resultType+"\n\n"+item.desc,
            insertText: key+"("+item.params+");",
            filterText: buildFilterText(key),
            insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
            range: range
        })
    });

    //全局CLASS类型 import的类
    regex = new RegExp("^(import) +.*[;]?",'gm');
    let matches = fullValue.match(regex);
    $.each(matches,function (index,item) {
        let key = item.replace(/import +/,"").replace(";","").trim();
        key = key.substring(key.lastIndexOf(".")+1);
        suggestions.push({
            label: key,
            kind: monaco.languages.CompletionItemKind.Variable,
            detail: item,
            insertText: key,
            filterText: buildFilterText(key),
            insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
            range: range
        })
    })
    return suggestions;
}

/**类的方法自动完善功能*/
function provideCompletionFunc(context,varName,lineContent,position){
    let suggestions = [];

    //全局内置变量
    let clazz = completionItems.variables[varName];
    parseClazz(clazz,suggestions,lineContent,position);

    //查找完整的调用链接
    if(!(clazz && clazz.length>0)) {//如果没有找到class，则在调用链接中找一下
        clazz = findClazzWithChain(context, varName, lineContent, position);
        parseClazz(clazz, suggestions, lineContent, position);
    }

    //上下文定义的变量
    if(!(clazz && clazz.length>0)){//如果没有找到class，则在上下文中找一下
        if(varName==')'){
            return suggestions;//如果是方法，则不处理
        }
        clazz = findClazz(context,varName);
        parseClazz(clazz,suggestions,lineContent,position);
    }

    //链式调用
    return suggestions;
}

function parseClazz(clazz,suggestions,lineContent,position) {
    if (!clazz)return;

    let methods = completionItems.clazzs[clazz];
    if (!methods || methods.length == 0){
        clazz = buildMethodsForClazz(clazz);
        methods = completionItems.clazzs[clazz];
        if (!methods || methods.length == 0){
            return;
        }
    }

    $.each(methods,function (index,item) {
        let label  = "";
        let insertText = "";
        let kind = monaco.languages.CompletionItemKind.Method;
        if (item.type == 'field'){
            label = item.varName +" ";
            insertText = item.varName;
            kind = monaco.languages.CompletionItemKind.Property;
        }else{
            label = item.varName +"("+item.params+") ";
            if (item.params){
                insertText = item.varName+"("+item.params+")";
            }else{
                insertText = item.varName+"()";
            }

        }
        suggestions.push({
            label: label,
            kind: kind,
            detail: ('static'==item.type?'static\t':'') + (!(item.resultType && item.resultType.length>0)?'' : item.resultType.substring(item.resultType.lastIndexOf(".")+1)+(item.desc && item.desc.length >0 ?(" \n\n"+item.desc):"")),
            filterText:buildFilterText(label),
            insertText: insertText,
            insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
        })
    });
}

/**
 * 根据完整的调用链接查找所属类。
 * @param context
 * @param varName
 * @param lineContent
 * @param position
 */
function findClazzWithChain(context,varName,lineContent,position) {
    let code = lineContent.substring(0,position.column-2);//取到当前光标之前的内容
    if(varName!=')' && code.lastIndexOf("."+varName)<1){
        //如果不是链式调用，则直接不处理了，返回null，让后面的逻辑处理
        return null;
    }
    code = code.replace(/\([^()]*\)/g,'()').replace(/ +/," ");//把方法里面的参数去掉，同时把多个空格转换成一个空格
    if(code.lastIndexOf(" ")>0){
        code = code.substring(code.lastIndexOf(" ")+1)
    }
    if(code.lastIndexOf("=")>0){
        code = code.substring(code.lastIndexOf("=")+1)
    }
    //从内置方法或内置变量里面一层层找
    let parts = code.split('.');
    let clazz = null;
    for (let i = 0; i < parts.length; i++) {
        let p = parts[i];
        if(clazz==null){//如果没有clazz，说明从头开始，查找一下。
            if(p.indexOf("()")>0){
                clazz = findFromMethods(p,completionItems.functions);
            }else{
                //当前光标前面的所有内容
                let lines = context.split("\n");
                let preContent = "";
                for (let j = 0; j < position.lineNumber; j++) {// 整个内部的最前面有一个隐藏的空行，所以这里就不减1了。
                    if(j==position.lineNumber-1){
                        //达到光标所在行，取光标前面的内容
                        preContent+="\n"+lines[j].substring(0,position.column);
                    }else{
                        preContent+="\n"+lines[j];
                    }
                }
                //在前面的内容里面找变量定义
                clazz = findClazz(preContent,p);
                if(!clazz){
                    clazz = completionItems.variables[p];
                }
            }
        }else{//从clazz中找到对应的属性或方法
            let methods = completionItems.clazzs[clazz];
            if (!methods || methods.length == 0){
                clazz = buildMethodsForClazz(clazz);
                methods = completionItems.clazzs[clazz];
                if (!methods || methods.length == 0){
                    return;
                }
            }
            clazz = findFromMethods(p,methods);
        }
        if(!clazz || clazz.length<1){
            return null;//如果clazz不存在，就要处理了
        }
    }
    return clazz;
}

/**
 * 在类的描述中查找到指定的方法或属性
 * @param name 方法或属性的名称
 * @param methods 方法集合
 */
function findFromMethods(name,methods) {
    let isField = name.indexOf("()")<0;//没有括号，表示是属性
    if(!isField){
        name = name.substring(0,name.length-2);
    }

    for (let i = 0; i < methods.length; i++) {
        let method = methods[i];

        let type = method.type;
        let methodName = method.varName;
        if(isField){
            if(type!='field'){
                continue;
            }
        }else{
            if(type=='field'){
                continue;
            }
        }
        if(methodName!=name){
            continue;
        }
        return method.resultType;
    }
    return null;
}
/**
 * 在整个代码区域中查找，有没有定义这个变量的
 * @param context
 * @param varName
 * @returns {null|*}
 */
function findClazz(context,varName) {
    let matchItem = context.match("[A-Z]{1}[a-zA-z]* +"+varName);
    let clazz = null;

    if (!matchItem){
        $.each(completionItems.clazzs,function (key,value) {
            if(key.substring(key.lastIndexOf(".")+1) == varName){
                clazz = key;
                return false;
            }
        })
        return clazz;
    }
    let clazzSimpleName = matchItem[0].substring(0,matchItem[0].indexOf(" "));
    let regex = new RegExp("^(import) +.*("+clazzSimpleName+")[;]?",'m');

    let importName = context.match(regex);
    if (importName){//如果在import中存在，则返回import中的值
        return importName[0].replace(/import +/,"").replace(";","").trim();
    }else{//从内置变量中找找看
        return completionItems.variables[clazzSimpleName];
    }
    return null;
}

function buildFilterText(label) {
    return label.split("").join(" ");
}
